Watching the File System
Reporting Acts of Creation
© 1999 by Andrew S. Downs
andrew@downs.net


Contents


Abstract

It is often useful to know when file-related actions occur. Typically, this requires either patching the File Manager or periodically searching one or more folders for items of interest. You then need to manipulate the resulting information and do something useful with it. This paper describes the design and implementation of a program which patches, processes, and presents information regarding file creation.

Introduction

In this paper, I describe the components of a system which can be used to obtain and relay information about file-related events to users. The term "user" will be used to refer to both end users and developers, unless specified otherwise.

Why do it?

Sometimes you want or need to know what is happening in the file system. However, the Macintosh file system (HFS and HFS+) does not contain ways to easily discover file-related events. (Folder actions in Mac OS 8.5 help somewhat.) You may wish to know when a file of a certain type is created, or when files in a particular folder get deleted, or maybe track what folder a user saves to most often (perhaps for the purpose of providing a helper application). These types of actions can form the basis of new products, or enhance an existing product. But to get that information, you need to interact with the file system.

How to do it?

The approach outlined in this paper consists of breaking down the information retrieval and notification process into manageable subprocesses, each of which can be addressed with a specific piece of code.

Overall, we want to:

Each of these activities or subprocesses can be handled with code. But, how best to organize such code?

Different components

Let's consider the first goal: obtaining information about file-related events. This is exactly what patches (in the form of system extensions) are all about. In our case, we do not want to manipulate results of any traps, but we do need to know whether an operation succeeded. This implies the following characteristics of the patch:

In order to massage the file information, we need an intermediary between the patch and the user. This piece of code interacts with the patch in order to obtain whatever information the patch has been able to discover. This requires that the patch and intermediary define a structure (or class) that can be used to pass file information.

Another, possibly stronger, reason for an intermediary, is to reduce the amount of work performed by the patch. This is especially critical for traps that are called often, or that might be executing 68k code. Performance suffers if the patch has to do a lot of work before returning. Providing an intermediary solves some of the performance problem.

The approach outlined in this paper uses a background application (typically of type 'appe') as an intermediary. Background apps should be unobtrusive and reliable, while still getting enough CPU time to do their work. Implementing and debugging background apps can be tricky; refer to Apple's Technote 1070 for details.

For exchanging data between the patch and background app, the File System Specification (FSSpec) record is nearly ideal. We will see that there are some additional fields that may be of interest. Wrapping the FSSpec in a new structure is one solution. Another is to use a Parameter Block Record (PBRec), which is how the low-level File Manager traps pass data. One issue with PBRecs is that they are unions of other record types, and so contain many additional fields that will not be of interest, resulting in wasted space.

A third component of our system is an interface with the user/developer. This can take one of several forms, depending on need. Four possible solutions are discussed later in this paper. Two of these (aliases and log files) are targeted at end users, while the others (Apple Events and callbacks) are intended for developer use.

Finally, there should be some way of controlling the action of the other components. A control panel is very useful for this purpose. The references listed in the bibliography provide a lot of detail on how to write control panels. Consequently, I will not address their implementation in this paper.

Figure 1 illustrates the various components of our system, and how they interact.

Figure 1. Components of our file system watcher.

Data flow

The patch obtains initial information about the file event by intercepting a File Manager call. It also gets the result code back from the File Manager upon completion of the original call. The patch hands the file information to the background app on request (when polled) or asynchronously (a push to a shared data area). Both methods work; each has its advantages.

The background app gets the raw data from the patch and performs additional file system queries in order to clarify or provide additional detail regarding the call. For instance, FSSpec information is not useful to the average user, but the path to the file is. Calling the File Manager to get the full path to a file specified in an FSSpec may be one of the background app's roles.

The interface to the user provides:

The relay of information to the user can be accomplished through one of these methods:

Other approaches can be devised, but only the four methods listed above will be discussed.

Ease of updating

Separating the components by function or purpose makes it relatively easy to update each piece as needed, and post those (smaller) updates to public archives or directly to registered users. A more compelling reason is that it is much easier as a developer to track changes in your code, and isolate testing, if you can focus on one or several small components rather than one monolothic application.

Items of interest

What, specifically, should our system be prepared to handle? Three possible requirements are what, where, and when an activity occured.

What happened

We need to get information about one or more of the following activities for files and/or folders:

Each of these can be implemented using a patch to one or more File Manager traps, either high- or low-level. We will look at the file create activity, but the same approach can be applied to the other three.

Where it happened

As stated earlier, FSSpecs are useful in further querying the File Manager, but paths are often more useful to the end user. Depending on what patches we've applied, and what we intend to tell the user, we need:

This applies to both files and folders.

When it happened

If we are simply providing an alias to a file, the operating system will ensure that the correct creation/modification date gets associated with the alias. But if we are writing to a log file, we also need:

The timestamp can be obtained by querying the File Manager or generating our own. The File Manager approach is more accurate, given potential system and network latency.

Next page